#!/bin/bash
# lprint - paginated plain-text printing to an Okidata Microline 92
#
# Version 0.97 2026-10-01
#  Written by Claude with direction from J R Casey Bralla
#
#
# Required prerequisites:
#       - openbsd-netcat
#
#

set -u

# =========================================================================
# DESTINATION
# =========================================================================

PRINTERHOST="${LPRINT_HOST:-DotMatrix.NerdWorld.org}"
PRINTERPORT="${LPRINT_PORT:-9100}"

# FQDN of the host requesting the print (shown right-justified in the
# header). Falls back to the short hostname, then to $HOSTNAME, for hosts
# whose "hostname" has no -f option or cannot resolve a qualified name.
REQUESTINGHOST="$(hostname -f 2>/dev/null)"
[ -n "$REQUESTINGHOST" ] || REQUESTINGHOST="$(hostname 2>/dev/null)"
[ -n "$REQUESTINGHOST" ] || REQUESTINGHOST="${HOSTNAME:-unknown}"

# =========================================================================
# PAGE LAYOUT
# =========================================================================

PAGELENGTH=66            # total lines per page: 11 inches @ 6 LPI
CHARACTERPITCH=10        # characters per inch: 10, 12 or 17
PAGEWIDTH=0              # columns; 0 means "derive from CHARACTERPITCH"
TABSTOPWIDTH=8           # columns per tab stop
APPENDCARRIAGERETURN=1   # 1 = end each line with CR LF, 0 = LF only
USEFORMFEEDS=0           # 0 = pad short pages with blank lines, 1 = send FF

# --- page furniture -------------------------------------------------------
# Lines consumed by everything that is not body text. BODYLINESPERPAGE is
# whatever is left over, so these may be edited freely without having to
# keep a separate total in step.
TOPMARGINLINES=2         # blank lines above the header
HEADERLINES=2            # "Filename: ... host" plus the rule under it
FOOTERGAPLINES=2         # blank lines between body text and the footer
FOOTERLINES=1            # "<file date> ... Page n"
BOTTOMMARGINLINES=5      # blank lines below the footer

PAGEFURNITURELINES=$(( TOPMARGINLINES + HEADERLINES + FOOTERGAPLINES \
                     + FOOTERLINES + BOTTOMMARGINLINES ))

# =========================================================================
# PRINTER CONTROL CODES
# =========================================================================

# Okidata MicroLine native pitch codes (single characters, not escapes)
PITCHCODE10CPI=$'\036'   # RS,  CHR(30) - pica,      10 cpi
PITCHCODE12CPI=$'\034'   # FS,  CHR(28) - elite,     12 cpi
PITCHCODE17CPI=$'\035'   # GS,  CHR(29) - condensed, 17.1 cpi

# Print quality. Correspondence is multi-pass (slower, denser). Data
# processing is single-pass (faster, lighter).
CORRESPONDENCEQUALITY=$'\033\061'    # ESC 1 = CHR(27) CHR(49)
DATAPROCESSINGQUALITY=$'\033\060'    # ESC 0 = CHR(27) CHR(48)

SELECTEDQUALITY="$CORRESPONDENCEQUALITY"

# The ML92 has no correspondence font at 17.1 cpi: while correspondence is
# selected the condensed command is silently discarded and the carriage
# keeps whatever pitch it already had. Measured from a 600 dpi greyscale
# scan of the -T sampler (40-bar rows, median inter-bar spacing):
#     correspondence 10 -> 10.00 cpi      data processing 10 -> 10.00 cpi
#     correspondence 12 -> 12.00 cpi      data processing 12 -> 12.00 cpi
#     correspondence 17 -> 12.00 cpi      data processing 17 -> 17.14 cpi
#                          ^^^^^ ignored
# So 17.1 cpi drops to data processing; see the CHARACTERPITCH case below.

# =========================================================================
# DIAGNOSTIC PAGES
# =========================================================================

PRINTALIGNMENTTEST=0     # set by -T
PRINTHEADSIGNATURE=0     # set by -S
PRINTCOMMANDPROBE=0      # set by -P
WRITETOSTDOUT=0          # set by -o: dump the byte stream instead of printing

# --- command probe page --------------------------------------------------
# Isolates WHICH control code the printer is ignoring, by sending each
# candidate sequence on its own line with a known 10 cpi reset line in
# front of it. Every probe line carries the same 60-column payload, so a
# line that took the condensed command ends visibly short of the others.
PROBERESETLINE='---------- reset to 10 cpi ----------'
PROBEPAYLOADWIDTH=60

# --- head alignment test page --------------------------------------------
# Prints identical glyphs in each quality at each pitch, so the two can be
# compared side by side on one sheet.
TESTBARLINE='||||||||||||||||||||||||||||||||||||||||'
TESTCAPITALLINE='EFHILMNTU EFHILMNTU EFHILMNTU EFHILMNTU'
BIDIRECTIONALTESTLINES=16    # lines per quality on test page 2

# --- head signature page (metrology) -------------------------------------
# Deliberately fixed geometry at 10 cpi regardless of -c/-e/-w, so that
# signature pages printed months apart stay directly comparable. 20 lines
# per quality x 10 isolated strokes per line = 200 strokes to average over,
# spread across the full carriage travel. The '=' rules are fiducials: they
# give the measuring software a long straight baseline to derive scan skew
# from, so scanner skew can be separated from real head tilt.
SIGNATURELINESPERQUALITY=20
SIGNATUREFIDUCIALRULE=$(printf '=%.0s' {1..80})
SIGNATUREBARLINE=$(printf '|       %.0s' {1..9})'|'

# =========================================================================
# COMMAND LINE
# =========================================================================

usage() {
    cat >&2 <<'EOF'
lprint - paginated plain-text printing to an Okidata Microline 92
          default 10 CPI (80 columns)

usage: lprint [options] [file ...]
       lprint [options] < file
       lprint -T | -S

With no file arguments lprint reads standard input. If standard input is
a terminal (i.e. no file and nothing piped in), this help is shown.

destination
  -h host   printer host or FQDN        (default $LPRINT_HOST)
  -p port   printer port                (default $LPRINT_PORT, else 9100)

layout
  -w cols   page width in columns       (default: derived from pitch)
  -l lines  page length in lines        (default 66 = 11in @ 6 LPI)
  -t n      tab stop width              (default 8)
  -F        send form feeds instead of padding with blank lines
  -n        do not append CR to line ends

pitch and quality
            default 10 CPI (80 columns)
  -e        elite, 12 cpi (96 columns)
  -c        condensed, 17.1 cpi (136 columns)
            implies -d: the ML92 has no correspondence font at this
            pitch, and asking for both makes it ignore the condensed
            command and print at whatever pitch it already had
  -d        data processing quality: single pass, faster, lighter
            (default is correspondence: multi-pass, slower, denser)

diagnostics
  -T        print the head alignment test page (2 pages) and exit
  -S        print the head signature page and exit
  -P        print the command probe page and exit: tests each candidate
            pitch/quality sequence separately to find which one the
            printer ignores
  -o        write the byte stream to standard output instead of sending
            it to the printer, for inspection:  lprint -o -c f | od -c

The signature page is a metrology target: scan it at 600 DPI in
GREYSCALE (not bilevel) and measure the lateral wander of the vertical
strokes. Print one before and one after any mechanical work on the head
or carriage to tell objectively whether the repair changed anything.
EOF
    exit 1
}

while getopts "h:p:w:l:t:cedFnTSPo" OPTION; do
    case "$OPTION" in
        h) PRINTERHOST="$OPTARG" ;;
        p) PRINTERPORT="$OPTARG" ;;
        w) PAGEWIDTH="$OPTARG" ;;
        l) PAGELENGTH="$OPTARG" ;;
        t) TABSTOPWIDTH="$OPTARG" ;;
        c) CHARACTERPITCH=17 ;;
        e) CHARACTERPITCH=12 ;;
        d) SELECTEDQUALITY="$DATAPROCESSINGQUALITY" ;;
        F) USEFORMFEEDS=1 ;;
        n) APPENDCARRIAGERETURN=0 ;;
        T) PRINTALIGNMENTTEST=1 ;;
        S) PRINTHEADSIGNATURE=1 ;;
        P) PRINTCOMMANDPROBE=1 ;;
        o) WRITETOSTDOUT=1 ;;
        *) usage ;;
    esac
done
shift $((OPTIND - 1))

# No files named and nothing piped in: the user wants help, not a hung
# read on the tty. A redirect or pipe still works, so `lprint < foo` and
# `cmd | lprint` are unaffected.
if [ "$PRINTALIGNMENTTEST" -eq 0 ] && [ "$PRINTHEADSIGNATURE" -eq 0 ] \
   && [ "$PRINTCOMMANDPROBE" -eq 0 ] && [ $# -eq 0 ] && [ -t 0 ]; then
    usage
fi

# =========================================================================
# DERIVED SETTINGS
# =========================================================================

case "$CHARACTERPITCH" in
    10) SELECTEDPITCHCODE="$PITCHCODE10CPI"; DEFAULTPAGEWIDTH=80  ;;
    12) SELECTEDPITCHCODE="$PITCHCODE12CPI"; DEFAULTPAGEWIDTH=96  ;;
    17) SELECTEDPITCHCODE="$PITCHCODE17CPI"; DEFAULTPAGEWIDTH=136 ;;
esac

# Condensed is only available in data processing quality. If correspondence
# were left selected the printer would discard the condensed command and
# print at the previous pitch while we formatted for 136 columns, so drop
# the quality to data processing whenever 17.1 cpi is in use. Done silently:
# it is the only quality this pitch has, so there is no choice to report.
if [ "$CHARACTERPITCH" -eq 17 ]; then
    SELECTEDQUALITY="$DATAPROCESSINGQUALITY"
fi

[ "$PAGEWIDTH" -eq 0 ] && PAGEWIDTH=$DEFAULTPAGEWIDTH

BODYLINESPERPAGE=$(( PAGELENGTH - PAGEFURNITURELINES ))
if [ "$BODYLINESPERPAGE" -lt 1 ]; then
    echo "lprint: page length too short" >&2
    exit 1
fi

# =========================================================================
# OUTPUT PLUMBING
# =========================================================================

sendtoprinter() {
    if command -v ncat >/dev/null 2>&1; then
        ncat --send-only -w 30 "$PRINTERHOST" "$PRINTERPORT"
    elif nc -h 2>&1 | grep -q -- '-N'; then
        nc -N -w 30 "$PRINTERHOST" "$PRINTERPORT"
    else
        nc -q 0 -w 30 "$PRINTERHOST" "$PRINTERPORT"
    fi
}

appendcarriagereturns() {
    if [ "$APPENDCARRIAGERETURN" -eq 1 ]; then sed -e 's/$/\r/'; else cat; fi
}

# -o diverts the finished byte stream to stdout so it can be inspected with
# od(1) without a printer attached.
outputdestination() {
    if [ "$WRITETOSTDOUT" -eq 1 ]; then cat; else sendtoprinter; fi
}

# =========================================================================
# DIAGNOSTIC PAGE BUILDERS
# =========================================================================

testblock() {   # $1 = quality code, $2 = pitch code, $3 = label
    local QUALITYCODE="$1" PITCHCODE="$2" LABEL="$3"
    printf '%s%s' "$QUALITYCODE" "$PITCHCODE"
    printf '%s\n' "$LABEL"
    printf '%s\n' "$TESTBARLINE"
    printf '%s\n' "$TESTCAPITALLINE"
    printf '%s\n' "$TESTBARLINE"
    printf '\n'
}

verticalruleblock() {   # $1 = quality code, $2 = label, $3 = line count
    local QUALITYCODE="$1" LABEL="$2" LINESWANTED="$3"
    local LINECOUNTER=0
    printf '%s%s' "$QUALITYCODE" "$PITCHCODE10CPI"
    printf '%s\n' "$LABEL"
    while [ "$LINECOUNTER" -lt "$LINESWANTED" ]; do
        printf '    ||%50s||\n' ''
        LINECOUNTER=$(( LINECOUNTER + 1 ))
    done
    printf '\n'
}

alignmenttestpage() {
    # --- page 1: quality and pitch sampler -----------------------------
    printf '%s%s' "$PITCHCODE10CPI" "$DATAPROCESSINGQUALITY"
    printf 'lprint alignment test\n'
    printf 'from: %s\n' "$REQUESTINGHOST"
    printf 'to:   %s\n\n' "$PRINTERHOST"
    testblock "$CORRESPONDENCEQUALITY" "$PITCHCODE10CPI" '[correspondence 10 cpi]'
    testblock "$DATAPROCESSINGQUALITY" "$PITCHCODE10CPI" '[data processing 10 cpi]'
    testblock "$CORRESPONDENCEQUALITY" "$PITCHCODE12CPI" '[correspondence 12 cpi]'
    testblock "$DATAPROCESSINGQUALITY" "$PITCHCODE12CPI" '[data processing 12 cpi]'
    testblock "$CORRESPONDENCEQUALITY" "$PITCHCODE17CPI" '[correspondence 17 cpi]'
    testblock "$DATAPROCESSINGQUALITY" "$PITCHCODE17CPI" '[data processing 17 cpi]'

    # Same pair of codes as the correspondence-17 block above, but with the
    # pitch issued BEFORE the quality instead of after. If this block comes
    # out at 17.1 cpi the drop is an ordering artefact and the two commands
    # can coexist; if it comes out at 12 cpi (matching the block above) the
    # font simply does not exist and 17.1 cpi must imply data processing.
    printf '%s%s' "$PITCHCODE17CPI" "$CORRESPONDENCEQUALITY"
    printf '%s\n' '[condensed set before correspondence]'
    printf '%s\n' "$TESTBARLINE"
    printf '%s\n' "$TESTCAPITALLINE"
    printf '%s\n' "$TESTBARLINE"
    printf '\014'

    # --- page 2: bidirectional registration ----------------------------
    # Consecutive lines print in opposite directions. Sight down the left
    # edge of each column of bars: a straight edge means the head registers
    # in both directions, a sawtooth (alternate lines offset) means
    # bidirectional misalignment. The right-hand column sits ~60 characters
    # out, so it shows any error that accumulates across carriage travel.
    printf '%s%s' "$PITCHCODE10CPI" "$DATAPROCESSINGQUALITY"
    printf 'bidirectional registration -- sight down the left edge of each column\n\n'
    verticalruleblock "$CORRESPONDENCEQUALITY" '[correspondence]'  "$BIDIRECTIONALTESTLINES"
    verticalruleblock "$DATAPROCESSINGQUALITY" '[data processing]' "$BIDIRECTIONALTESTLINES"
    printf '\014'
}

signatureblock() {   # $1 = quality code, $2 = label
    local QUALITYCODE="$1" LABEL="$2"
    local LINECOUNTER=0
    printf '%s%s' "$QUALITYCODE" "$PITCHCODE10CPI"
    printf '%s\n' "$LABEL"
    while [ "$LINECOUNTER" -lt "$SIGNATURELINESPERQUALITY" ]; do
        printf '%s\n' "$SIGNATUREBARLINE"
        LINECOUNTER=$(( LINECOUNTER + 1 ))
    done
}

# The reset line restores BOTH pitch and quality to the power-on state, so
# each probe is independent. Resetting only the pitch lets a quality set by
# one probe leak into the next, which made the first version of this page
# report every line after P6 as "light" regardless of what it was testing.
probeline() {   # $1 = control codes under test, $2 = label
    local CONTROLCODES="$1" LABEL="$2"
    printf '%s%s%s\n' "$PITCHCODE10CPI" "$CORRESPONDENCEQUALITY" "$PROBERESETLINE"
    printf "%s%-${PROBEPAYLOADWIDTH}s\n" "$CONTROLCODES" "$LABEL"
}

commandprobepage() {
    printf '%s%s' "$PITCHCODE10CPI" "$DATAPROCESSINGQUALITY"
    printf 'lprint command probe\n'
    printf 'date: %s\n' "$(date '+%Y-%m-%d %H:%M')"
    printf 'from: %s\n\n' "$REQUESTINGHOST"
    printf 'Each probe is preceded by a reset to 10 cpi correspondence, so\n'
    printf 'the probes are independent of each other. Every P line carries\n'
    printf 'the same 60-column payload, so a line that accepted condensed\n'
    printf 'ends well short of the reset line above it.\n'
    printf 'Note which P lines are SHORT, and which look lighter (draft).\n\n'

    # The sequence lprint itself sends.
    probeline "$DATAPROCESSINGQUALITY$PITCHCODE17CPI" \
              'P1 ESC0 then GS - the sequence lprint sends'
    # Same two codes the other way round.
    probeline "$PITCHCODE17CPI$DATAPROCESSINGQUALITY" \
              'P2 GS then ESC0 - reversed order'
    # Condensed with no quality command anywhere near it.
    probeline "$PITCHCODE17CPI" \
              'P3 GS alone - no quality command at all'
    # Known-bad control: correspondence should refuse condensed.
    probeline "$CORRESPONDENCEQUALITY$PITCHCODE17CPI" \
              'P4 ESC1 then GS - expected NOT condensed'
    # Pitch sanity check: if this is not narrower, no pitch code works.
    probeline "$PITCHCODE12CPI" \
              'P5 FS alone - 12 cpi, pitch sanity check'
    # Quality sanity check: should stay 10 cpi but print lighter.
    probeline "$DATAPROCESSINGQUALITY" \
              'P6 ESC0 alone - 10 cpi but draft weight'
    # ESC/P condensed, in case this printer has an alternate PROM fitted
    # and is not listening to the native Okidata pitch codes.
    probeline $'\017' \
              'P7 SI 0x0F - ESC/P condensed, alternate PROM'

    # The two codes split across separate lines, in case the printer will
    # not accept a quality and a pitch change within one line.
    printf '%s%s%s\n' "$PITCHCODE10CPI" "$CORRESPONDENCEQUALITY" "$PROBERESETLINE"
    printf "%s%-${PROBEPAYLOADWIDTH}s\n" "$DATAPROCESSINGQUALITY" \
           'P8a ESC0 on this line, GS on the next'
    printf "%s%-${PROBEPAYLOADWIDTH}s\n" "$PITCHCODE17CPI" \
           'P8b GS on its own line - split across lines'
    printf '\014'
}

headsignaturepage() {
    printf '%s%s' "$PITCHCODE10CPI" "$DATAPROCESSINGQUALITY"
    printf 'lprint head signature\n'
    printf 'date: %s\n' "$(date '+%Y-%m-%d %H:%M')"
    printf 'from: %s\n' "$REQUESTINGHOST"
    printf 'to:   %s\n' "$PRINTERHOST"
    printf 'scan this at 600 DPI GREYSCALE -- not bilevel\n\n'
    printf '%s\n' "$SIGNATUREFIDUCIALRULE"
    signatureblock "$CORRESPONDENCEQUALITY" '[correspondence]'
    printf '%s%s\n' "$DATAPROCESSINGQUALITY" "$SIGNATUREFIDUCIALRULE"
    signatureblock "$DATAPROCESSINGQUALITY" '[data processing]'
    printf '%s%s\n' "$DATAPROCESSINGQUALITY" "$SIGNATUREFIDUCIALRULE"
    printf '\014'
}

# =========================================================================
# BODY TEXT FORMATTING
# =========================================================================

filedatestamp() {   # $1 = file; prints the footer's left-hand date string
    local BIRTHTIME
    BIRTHTIME=$(stat -c %W -- "$1" 2>/dev/null)
    case "$BIRTHTIME" in
        ''|*[!0-9]*|0) printf 'Modified %s' "$(date -r "$1" '+%Y-%m-%d %H:%M')" ;;
        *)             printf 'Created  %s' "$(date -d "@$BIRTHTIME" '+%Y-%m-%d %H:%M')" ;;
    esac
}

formatpages() {   # $1 = name for header, $2 = date string for footer
    expand -t "$TABSTOPWIDTH" | awk \
        -v HEADERFILENAME="$1" \
        -v FOOTERDATE="$2" \
        -v PAGECONTROLCODES="$SELECTEDQUALITY$SELECTEDPITCHCODE" \
        -v REQUESTINGHOST="$REQUESTINGHOST" \
        -v PAGEWIDTH="$PAGEWIDTH" \
        -v BODYLINESPERPAGE="$BODYLINESPERPAGE" \
        -v TOPMARGINLINES="$TOPMARGINLINES" \
        -v FOOTERGAPLINES="$FOOTERGAPLINES" \
        -v BOTTOMMARGINLINES="$BOTTOMMARGINLINES" \
        -v USEFORMFEEDS="$USEFORMFEEDS" '

    # Return a string of COUNT spaces.
    function spaces(COUNT,   RESULT, COUNTER) {
        RESULT = ""
        for (COUNTER = 0; COUNTER < COUNT; COUNTER++) RESULT = RESULT " "
        return RESULT
    }

    # Print COUNT blank lines.
    function blanklines(COUNT,   COUNTER) {
        for (COUNTER = 0; COUNTER < COUNT; COUNTER++) print ""
    }

    # Top margin, "Filename: <name>" left with the requesting host right
    # justified against it, then a rule the full width of the page.
    #
    # PAGECONTROLCODES (quality + pitch) is emitted at the head of the
    # header line rather than once at the start of the job. The ML92 only
    # commits a pending pitch or quality change when it prints a line that
    # has content in it; codes sent ahead of the blank top-margin lines are
    # flushed away before any text appears, leaving the whole job at
    # whatever the previous job happened to set. Putting them here
    # guarantees printable text immediately follows, and re-asserts them on
    # every page. They occupy no columns on the page, so they are excluded
    # from the width arithmetic above and prepended only at print time.
    function startpage(   LEFTTEXT, RIGHTTEXT, GAPWIDTH) {
        PAGENUMBER++
        blanklines(TOPMARGINLINES)
        LEFTTEXT = "Filename: " HEADERFILENAME
        RIGHTTEXT = REQUESTINGHOST
        if (length(LEFTTEXT) + length(RIGHTTEXT) + 1 > PAGEWIDTH)
            LEFTTEXT = substr(LEFTTEXT, 1, PAGEWIDTH - length(RIGHTTEXT) - 1)
        GAPWIDTH = PAGEWIDTH - length(LEFTTEXT) - length(RIGHTTEXT)
        if (GAPWIDTH < 1) GAPWIDTH = 1
        print PAGECONTROLCODES LEFTTEXT spaces(GAPWIDTH) RIGHTTEXT
        print substr(RULELINE, 1, PAGEWIDTH)
        LINESUSEDONPAGE = 0
    }

    # Pad the body out to full depth, then the footer and bottom margin.
    function finishpage(   LEFTTEXT, RIGHTTEXT, GAPWIDTH) {
        while (LINESUSEDONPAGE < BODYLINESPERPAGE) { print ""; LINESUSEDONPAGE++ }
        blanklines(FOOTERGAPLINES)
        LEFTTEXT = FOOTERDATE
        RIGHTTEXT = "Page " PAGENUMBER
        GAPWIDTH = PAGEWIDTH - length(LEFTTEXT) - length(RIGHTTEXT)
        if (GAPWIDTH < 1) GAPWIDTH = 1
        print LEFTTEXT spaces(GAPWIDTH) RIGHTTEXT
        if (USEFORMFEEDS) printf "\014"; else blanklines(BOTTOMMARGINLINES)
    }

    BEGIN {
        RULELINE = ""
        while (length(RULELINE) < PAGEWIDTH) RULELINE = RULELINE "-"
        PAGENUMBER = 0
        LINESUSEDONPAGE = BODYLINESPERPAGE   # forces a page break on line 1
        PAGESTARTED = 0
        LINENUMBER = 0
        TEXTWIDTH = PAGEWIDTH - 5            # 5 columns hold the line number
    }

    {
        LINENUMBER++
        TEXT = $0
        sub(/\r$/, "", TEXT)
        FIRSTCHUNK = 1
        # Long lines wrap onto continuation rows, which are left unnumbered.
        do {
            if (!PAGESTARTED || LINESUSEDONPAGE >= BODYLINESPERPAGE) {
                if (PAGESTARTED) finishpage()
                startpage()
                PAGESTARTED = 1
            }
            CHUNK = substr(TEXT, 1, TEXTWIDTH)
            TEXT = substr(TEXT, TEXTWIDTH + 1)
            if (FIRSTCHUNK) { printf "%4d %s\n", LINENUMBER, CHUNK; FIRSTCHUNK = 0 }
            else              printf "     %s\n", CHUNK
            LINESUSEDONPAGE++
        } while (length(TEXT) > 0)
    }

    END {
        if (!PAGESTARTED) startpage()
        finishpage()
    }'
}

# =========================================================================
# MAIN
# =========================================================================

{
    if [ "$PRINTALIGNMENTTEST" -eq 1 ]; then
        alignmenttestpage | appendcarriagereturns
    elif [ "$PRINTHEADSIGNATURE" -eq 1 ]; then
        headsignaturepage | appendcarriagereturns
    elif [ "$PRINTCOMMANDPROBE" -eq 1 ]; then
        commandprobepage | appendcarriagereturns
    else
        # Also sent here so a printer that does honour codes at job start
        # is set up before the top margin. formatpages() re-asserts them on
        # every header line, which is what actually makes it stick.
        printf '%s' "$SELECTEDQUALITY"
        printf '%s' "$SELECTEDPITCHCODE"
        if [ $# -eq 0 ]; then
            formatpages "(stdin)" "Printed  $(date '+%Y-%m-%d %H:%M')"
        else
            for INPUTFILE; do
                if [ ! -r "$INPUTFILE" ]; then
                    echo "lprint: cannot read $INPUTFILE" >&2
                    continue
                fi
                formatpages "$INPUTFILE" "$(filedatestamp "$INPUTFILE")" < "$INPUTFILE"
            done
        fi | appendcarriagereturns
    fi
} | outputdestination
